package model;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.logging.Level;

import javax.swing.SwingUtilities;

import model.time.RestartTimerTask;

import org.apache.commons.io.FileUtils;

import util.IModel;
import util.IView;
import util.LoggingUtils;
import util.PlayerUtilities;

public class Preference implements IModel {
	private static final String BACKUP_FILE_EXTENSION = ".bak";
	
	private static final int DEFAULT_MEMORY_SIZE = 512;
	private static final String DEFAULT_ROOT_PASSWORD = "street";
	private static final int DEFAULT_UPDATE_FREQUENCY = 60;
	private static final double DEFAULT_SCALE = 0.3;
	private static final int DEFAULT_BOX_HEIGHT = 1920;
	private static final int DEFAULT_BOX_WIDTH = 1080;
	private static final int DEFAULT_BOX_ID = 0;
	public static final double MIN_SCALE = 0.1;
	public static final double MAX_SCALE = 1;
	public static final String DEFAULT_SCHEDULE_TIME = "03:00:00";
	public static final int DEFAULT_SCHEDULE_DAY = 1;
	private int boxID; // box id of the player
	private int playerWidth; // width of the player
	// window
	private int playerHeight; // height of the player
	// window
	private double scale; // scaling factor for
	// displaying the player
	private int updateCheckerFrequency; // frequency for
	// checking updates, in
	// seconds
	private boolean ftpTransfer; // if true, transfer
	// files by ftp,
	// otherwise
	// transfer by http
	private boolean debugEnabled; // show debug messages
	// if enabled
	private boolean clearContentOnStartup; // does the player clear
	// all content
	// on start up
	private boolean resendContentOnStartup; // does the player
	// resend content on
	// start up
	private boolean checkUpdates; // player will not check
	// updates if this
	// option
	// is false
	private boolean checkLogging; // Play will check for logging enabled or disabled
	
	private boolean eheckEmailSending;
	
	private boolean sendPlaylistRecord; // send the playlist
	// record id to server
	// to check if player
	// froze
	private boolean uploadLogs;
	private boolean zeroPauseTime; // if true, player will
	// not pause
	private boolean transitionEnabled; // player will not play
	// any transition

	private int numDaysToKeepLog;

	private String rootPassword;
	// TODO overriding option if file was not modified on remote server.

	// singleton instance of preference
	public static Preference _instance;

	// the views listening to this model
	private List<IView> views;

	private int memorySize;

	private String scheduleTime;
	private int scheduleday;
	
	private boolean playerUpgraded;

	private Preference() {
		this.boxID = DEFAULT_BOX_ID;
		this.playerWidth = DEFAULT_BOX_WIDTH;
		this.playerHeight = DEFAULT_BOX_HEIGHT;
		this.scale = DEFAULT_SCALE;
		this.updateCheckerFrequency = DEFAULT_UPDATE_FREQUENCY;
		this.ftpTransfer = true;
		this.debugEnabled = true;
		this.checkUpdates = true;
		this.checkLogging = true;
		this.eheckEmailSending = true;
		this.sendPlaylistRecord = false;
		this.uploadLogs = false;
		this.resendContentOnStartup = false;
		this.clearContentOnStartup = false;
		this.zeroPauseTime = false;
		this.transitionEnabled = true;
		this.rootPassword = DEFAULT_ROOT_PASSWORD;
		this.memorySize = DEFAULT_MEMORY_SIZE;
		this.playerUpgraded = true;
		this.scheduleTime	 = DEFAULT_SCHEDULE_TIME;
		this.scheduleday = DEFAULT_SCHEDULE_DAY;
		this.initializeOnLoad();
	}

	public void initializeOnLoad() {
		this.views = Collections.synchronizedList(new ArrayList<IView>());
	}

	public synchronized static Preference getInstance() {
		if (_instance == null) {
			// load the preference from disk first, if not found, create a new one
			Object obj = PlayerUtilities.getObjectFromXMLFile(Settings.PREFERENCES_XML);
			if (obj != null && obj instanceof Preference) {
				_instance = (Preference) obj;
			} else if(new File(Settings.PREFERENCES_XML+BACKUP_FILE_EXTENSION).exists()) {
				obj = PlayerUtilities.getObjectFromXMLFile(Settings.PREFERENCES_XML+BACKUP_FILE_EXTENSION);
				if (obj != null && obj instanceof Preference) {
					// loaded a backup copy of the preference file, make the player resend content to recover missing files.
					System.out.println("loading backup preference file.");
					_instance = (Preference) obj;
					_instance.setResendContentOnStartup(true);
				} else {
					_instance = new Preference();
				}
			} else {
				_instance = new Preference();
			}
			_instance.initializeOnLoad();
		}
		return _instance;
	}

	public synchronized boolean save() {
		if(PlayerUtilities.saveObjectToXMLFile(Settings.PREFERENCES_XML, this)) {
			try {
				FileUtils.copyFile(new File(Settings.PREFERENCES_XML), new File(Settings.PREFERENCES_XML+BACKUP_FILE_EXTENSION));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				String classMethod = "model.Preference in .save()";
				LoggingUtils.logStart(classMethod);
				LoggingUtils.log(Level.SEVERE, "Error in copying preference.xml file  "+e.getMessage() + classMethod,e);
				
				e.printStackTrace();
			}
			return true;
		}
		return false;
	}

	public int getBoxID() {
		return this.boxID;
	}

	public void setBoxID(int boxID) {
		this.boxID = boxID;
	}

	public int getPlayerWidth() {
		return this.playerWidth;
	}

	public void setPlayerWidth(int playerWidth) {
		this.playerWidth = playerWidth;
	}

	public int getPlayerHeight() {
		return this.playerHeight;
	}

	public void setPlayerHeight(int playerHeight) {
		this.playerHeight = playerHeight;
	}

	public double getScale() {
		return this.scale;
	}

	public void setScale(double scale) {
		if (scale < Preference.MIN_SCALE) {
			this.scale = Preference.MIN_SCALE;
		} else if (scale > Preference.MAX_SCALE) {
			this.scale = Preference.MAX_SCALE;
		} else {
			this.scale = scale;
		}
	}

	public int getUpdateCheckerFrequency() {
		return this.updateCheckerFrequency;
	}

	public void setUpdateCheckerFrequency(int updateCheckerFrequency) {
		this.updateCheckerFrequency = updateCheckerFrequency;
	}

	public boolean isFtpTransfer() {
		return this.ftpTransfer;
	}

	public void setFtpTransfer(boolean ftpTransfer) {
		this.ftpTransfer = ftpTransfer;
	}

	public boolean isDebugEnabled() {
		return this.debugEnabled;
	}

	public void setDebugEnabled(boolean debugEnabled) {
		this.debugEnabled = debugEnabled;
	}

	public boolean isZeroPauseTime() {
		return this.zeroPauseTime;
	}

	public void setZeroPauseTime(boolean zeroPauseTime) {
		this.zeroPauseTime = zeroPauseTime;
	}

	public boolean isTransitionEnabled() {
		return this.transitionEnabled;
	}

	public void setTransitionEnabled(boolean noTransition) {
		this.transitionEnabled = noTransition;
	}

	public boolean isClearContentOnStartup() {
		return this.clearContentOnStartup;
	}

	public void setClearContentOnStartup(boolean clearContentOnStartup) {
		this.clearContentOnStartup = clearContentOnStartup;
	}

	public boolean isResendContentOnStartup() {
		return this.resendContentOnStartup;
	}

	public void setResendContentOnStartup(boolean resendContentOnStartup) {
		this.resendContentOnStartup = resendContentOnStartup;
	}

	public boolean isCheckUpdates() {
		return this.checkUpdates;
	}
	
	public boolean isCheckLogging()
	{
		return this.checkLogging;
	}
	
	public void setCheckLogging(boolean checkLogging) {
		this.checkLogging = checkLogging;
	}
		
	public boolean isEheckEmailSending() {
		return eheckEmailSending;
	}

	public void setEheckEmailSending(boolean eheckEmailSending) {
		this.eheckEmailSending = eheckEmailSending;
	}

	public void setCheckUpdates(boolean checkUpdates) {
		this.checkUpdates = checkUpdates;
	}

	public boolean isSendPlaylistRecord() {
		return this.sendPlaylistRecord;
	}

	public void setSendPlaylistRecord(boolean sendPlaylistRecord) {
		this.sendPlaylistRecord = sendPlaylistRecord;
	}

	public boolean isUploadLogs() {
		return this.uploadLogs;
	}

	public void setUploadLogs(boolean updateLogs) {
		this.uploadLogs = updateLogs;
	}
	
	public void getRestartDuration(int day,String scheduleTime)
	{
		if(day == 0)
		{
			return;
		}
		else if(day == 2)
		{
			day = 7;
		}
		else if(day == 3)
		{
			day = 23;
		}
		
		
		String array[] = scheduleTime.split(":");
		
		int hour = 3;
		int minutes = 0;
		int second = 0;
		
		try
		{
			hour = Integer.parseInt(array[0]);
			minutes = Integer.parseInt(array[1]);
			second = Integer.parseInt(array[2]);
		}
		catch(Exception e)
		{
			hour = 3;
			minutes = 0;
			second = 0;
		}
		
		System.out.println("hour= " + hour+", Minutes = "+minutes+", second = "+second);
		
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.DATE, day);
		calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), hour, minutes, second);
		
		long added = calendar.getTimeInMillis();
		SimpleDateFormat sdf = new SimpleDateFormat("MMM dd,yyyy HH:mm");
		Date resultdate = new Date(added);
		LoggingUtils.log(Level.SEVERE, "Player is scheduled to restart at "+sdf.format(resultdate));
		System.out.println(sdf.format(resultdate));
		
		Timer restartTimer = new Timer();
		restartTimer.schedule(new RestartTimerTask(),calendar.getTime(),day*24*60*60*1000);
	}
	
	public String[] getScheduleTime(String scheduleTime)
	{
		String splited[] = scheduleTime.split(":");
		return splited;
	}

	@Override
	public void addView(IView view) {
		synchronized (this.views) {
			if (!this.views.contains(view)) {
				this.views.add(view);
			}
		}
	}

	@Override
	public void removeView(IView view) {
		synchronized (this.views) {
			if (this.views.contains(view)) {
				this.views.remove(view);
			}
		}
	}

	@Override
	public void removeAllViews() {
		synchronized (this.views) {
			try
			{
				this.views.clear();
			}
			catch(Exception e)
			{
				System.out.println("Exception in clearing views from preference.java removeallviews()");
			}
		}
	}

	/*@Override
	public void notifyViews() {
		synchronized (this.views) {
			for (IView view : this.views) {
				view.updateView(this, null);
			}
		}
	}*/
	
	@SuppressWarnings("unused")
	private void notifyLinuxBrowserViews()
	{
		for (IView view : this.views) {
			view.updateView(this, null);
		}
	}
	
	@Override
	public void notifyViews() {
	
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				synchronized (views) {
					for (IView view : views) {
						view.updateView(this, null);
					}
				}
			}
		});
			
	}
	
	public int getNumDaysToKeepLog() {
		return this.numDaysToKeepLog;
	}

	public void setNumDaysToKeepLog(int numDaysToKeepLog) {
		this.numDaysToKeepLog = numDaysToKeepLog;
	}

	public String getRootPassword() {
		if (this.rootPassword == null) {
			this.rootPassword = Preference.DEFAULT_ROOT_PASSWORD;
		}
		return this.rootPassword;
	}

	public int getMemorySize() {
		if(this.memorySize<256) {
			this.setMemorySize(256);
		}
		return memorySize;
	}

	public void setMemorySize(int memorySize) {
		if(memorySize<=256) {
			this.memorySize = 256;
			return;
		}
		this.memorySize = memorySize;
	}

	public boolean isPlayerUpgraded() {
		return this.playerUpgraded;
	}
	
	public void setPlayerUpgraded(boolean b) {
		this.playerUpgraded = b;
	}

	public String getScheduleTime() {
		return scheduleTime;
	}

	public void setScheduleTime(String scheduleTime) {
		this.scheduleTime = scheduleTime;
	}

	public int getScheduleday() {
		return scheduleday;
	}

	public void setScheduleday(int scheduleday) {
		this.scheduleday = scheduleday;
	}
}